///  <copyright file="MallowMain.cs" company="Infozoom" author="Stefan Zschocke">
///    You must not remove this notice, or any other, from this software.
///    Otherwise do with it whatever you want.
///    infoZoom may not be made liable for any damages whatsoever 
///    (including, without limitation, damages for loss of business profits, 
///    business interruption, loss of business information or other pecuniary loss)
///    arising out of use of or inability to use the Software
/// </copyright>
using System.Collections;
using System.IO;
using System.Xml;

namespace Mallow
{
    /// <summary>
    /// Summary description for Directory.
    /// </summary>
    internal class Directory
    {
        ArrayList mChildren = new ArrayList();
        ArrayList mComponents = new ArrayList();
        XmlNode mNode;
        Directory mParent;
        Mallow mMallow;
        bool mFound;
        string mDirName;
        bool mNew;

        internal Directory(Mallow mallow, Directory parent, XmlNode node, bool isNew)
        {
            mMallow = mallow;
            mParent = parent;
            mNode = node;
            mFound = isNew;
            mNew = isNew;
            RecursiveAddChildrenAndComponents();
        }

        internal virtual RootDirectory Root { get { return mParent.Root; } }
        internal Directory Parent { get { return mParent; } }
        internal IComparer FileNameComparer { get { return mMallow.FileNameComparer; } }
        internal bool IsNew { get { return mNew; } }
        internal bool Found { get { return mFound; } }

        internal string GetFullRelativeDirectoryName()
        {
            string name = mParent == null || mParent == Root ? "" : mParent.GetFullRelativeDirectoryName();
            if (Root != this)
            {
                if (name.Length > 0) name += "\\";
                name += DirectoryName;
            }
            return name;
        }

        internal string[] GetRelativeNames()
        {
            int entryCnt = 0;
            for (Directory cur = this; cur != null; cur = cur.Parent)
            {
                entryCnt++;
            }
            string[] n = new string[entryCnt];
            for (Directory cur = this; cur != null; cur = cur.Parent)
            {
                n[--entryCnt] = cur.DirectoryName;
            }
            return n;
        }

        void RecursiveAddChildrenAndComponents()
        {
            foreach (XmlNode child in mNode.ChildNodes)
            {
                if (child.Name.Equals("Directory"))
                {
                    mChildren.Add(new Directory(mMallow, this, child, false));
                }
                else if (child.Name.Equals("Component"))
                {
                    mComponents.Add(new Component(mMallow, this, child, false));
                }
            }
        }

        internal XmlNode Node { get { return mNode; } }

        internal void RecursiveSynchChildrenAndComponentsPass1(DirectoryInfo dirInfo, ProtocolBuilder protocol, string dirAlias)
        {
            mFound = true;
            FileInfo[] fileInfos = dirInfo.GetFiles();
            foreach (FileInfo fileInfo in fileInfos)
            {
                bool found = false;
                foreach (Component component in mComponents)
                {
                    found = component.FindFile(fileInfo);
                    if (found)
                    {
                        break;
                    }
                }
                if (!found)
                {
                    Component component = null;
                    if (mComponents.Count == 0 || mMallow.ConfigMultiFilesPerComponent == false)
                    {
                        XmlNode node = mMallow.CreateComponentNode(this, fileInfo.Name);
                        XmlNode curLastComponentNode = null;
                        XmlNode curFirstDirNode = null;
                        foreach (XmlNode curNode in mNode.ChildNodes)
                        {
                            if (curNode.Name == "Component")
                            {
                                curLastComponentNode = curNode;
                            }
                            else if (curFirstDirNode == null && curNode.Name == "Directory")
                            {
                                curFirstDirNode = curNode;
                            }
                        }

                        if (curLastComponentNode != null)
                        {
                            mNode.InsertAfter(node, curLastComponentNode);
                        }
                        else if (curFirstDirNode != null)
                        {
                            mNode.InsertBefore(node, curFirstDirNode);
                        }
                        else
                        {
                            mNode.AppendChild(node);
                        }
                        component = new Component(mMallow, this, node, true);
                        mComponents.Add(component);
                    }
                    else
                    {
                        component = (Component)mComponents[0];
                    }
                    XmlNode fileNode = mMallow.CreateFileNode(this, fileInfo, mMallow.ConfigMultiFilesPerComponent == false, dirAlias == null ? null : dirAlias + fileInfo.Name);
                    component.AddNewFile(fileNode);
                }
            }

            DirectoryInfo[] dirInfos = dirInfo.GetDirectories();
            foreach (DirectoryInfo childDir in dirInfos)
            {
                bool found = false;
                foreach (Directory directory in mChildren)
                {
                    found = FileNameComparer.Compare(directory.DirectoryName, childDir.Name) == 0;
                    if (found)
                    {
                        directory.RecursiveSynchChildrenAndComponentsPass1(childDir, protocol, dirAlias == null ? null : dirAlias + childDir.Name + "\\");
                        break;
                    }
                }
                if (!found)
                {
                    XmlNode node = mMallow.CreateDirectoryNode(this, childDir);
                    mNode.AppendChild(node);
                    Directory newChild = new Directory(mMallow, this, node, true);
                    mChildren.Add(newChild);
                    newChild.RecursiveSynchChildrenAndComponentsPass1(childDir, protocol, dirAlias == null ? null : dirAlias + childDir.Name + "\\");
                }
            }
        }

        internal void RecursiveSynchChildrenAndComponentsPass2(ProtocolBuilder protocol)
        {
            foreach (Component component in mComponents)
            {
                bool componentNeeded = false;
                component.Synch(protocol, out componentNeeded);
                if (!componentNeeded && this.Found) mNode.RemoveChild(component.Node);// if(!Found) parent will remove me together with my children
            }

            if (!Found)
            {
                protocol.DirectoryRemoved(this);
            }
            else if (IsNew)
            {
                protocol.DirectoryAdded(this);
            }

            foreach (Directory directory in mChildren)
            {
                directory.RecursiveSynchChildrenAndComponentsPass2(protocol);
                if (!directory.Found && this.Found)
                {
                    mNode.RemoveChild(directory.Node);
                }
            }
        }

        internal string DirectoryName
        {
            get
            {
                if (mDirName == null)
                {
                    XmlAttribute a = mNode.Attributes["LongName"];
                    if (a == null)
                    {
                        a = mNode.Attributes["Name"];
                    }
                    mDirName = a == null ? "" : a.Value;
                }
                return mDirName;
            }
        }
    }

    internal class RootDirectory : Directory
    {
        internal RootDirectory(Mallow mallow, XmlNode node, bool isNew) : base(mallow, null, node, isNew)
        {
        }

        internal void StartSynch(string startFileDir, ProtocolBuilder protocol, string dirAlias)
        {
            DirectoryInfo dirInfo = new DirectoryInfo(startFileDir);
            if (dirAlias != null && !(startFileDir.EndsWith("\\") || startFileDir.EndsWith("/")))
            {
                dirAlias += "\\";
            }
            RecursiveSynchChildrenAndComponentsPass1(dirInfo, protocol, dirAlias);
            RecursiveSynchChildrenAndComponentsPass2(protocol);
        }

        internal override RootDirectory Root { get { return this; } }
    }
}
